#pragma once

#include <boost/circular_buffer.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/thread/thread.hpp>
#include <boost/timer/timer.hpp>
#include <boost/call_traits.hpp>
#include <boost/bind.hpp>
#include <deque>
#include <list>
#include <string>
#include <iostream>

namespace Socket
{
template <class T>
class BufferBound : private boost::noncopyable
{
public:
    typedef boost::circular_buffer<T> container_type;
    typedef typename container_type::size_type size_type;
    typedef typename container_type::value_type value_type;
    typedef typename boost::call_traits<value_type>::param_type param_type;

    explicit BufferBound(size_type capacity) : m_container(capacity) 
    {
        std::cout << "BufferBound Initial size: " << capacity << std::endl;
    }

    void Push(param_type item)
    {
        boost::unique_lock<boost::mutex> lock(m_mutex);
        m_not_full.wait(lock, boost::bind(&BufferBound<value_type>::is_not_full, this));
        m_container.push_back(item);
        // std::cout << "Push value: " << (item) << std::endl;
        lock.unlock();
        m_not_empty.notify_one();
    }

    void Pop(value_type *pItem)
    {
        boost::unique_lock<boost::mutex> lock(m_mutex);
        m_not_empty.wait(lock, boost::bind(&BufferBound<value_type>::is_not_empty, this));

        *pItem = m_container.front();
        m_container.pop_front();

        lock.unlock();
        m_not_full.notify_one();
    }

    void show()
    {
        for (typename boost::circular_buffer<T>::iterator it = m_container.begin();it != m_container.end(); it++)
        {
            std::cout << (*it) << "  ";
        }
        std::cout << ", size: " << m_container.size() << std::endl;
    }

    bool is_not_empty() const 
    { 
        return !m_container.empty(); 
    }

    bool is_not_full() const 
    { 
        return m_container.size() < m_container.capacity();
    }

private:
    BufferBound(const BufferBound &) = default;
    BufferBound &operator=(const BufferBound &) = default;

    container_type m_container;
    boost::mutex m_mutex;
    boost::condition_variable m_not_empty;
    boost::condition_variable m_not_full;
};
}
